"""
依赖注入容器

实现依赖注入容器的核心功能。
"""

import threading
from typing import Any, Dict, List, Optional, Type, TypeVar, Union, Callable
from .tokens import Token, create_token
from .providers import Provider, ClassProvider, FactoryProvider, ValueProvider
from .scopes import Scope, scope_manager

T = TypeVar('T')


class DependencyError(Exception):
    """依赖注入错误"""
    pass


class CircularDependencyError(DependencyError):
    """循环依赖错误"""
    pass


class Container:
    """
    依赖注入容器
    
    管理对象的创建、依赖解析和生命周期。
    """
    
    def __init__(self, parent: Optional['Container'] = None):
        self.parent = parent
        self._bindings: Dict[Token, Provider] = {}
        self._scopes: Dict[Token, str] = {}
        self._instances: Dict[Token, Any] = {}
        self._lock = threading.RLock()
        # 使用线程本地存储来维护每个线程的循环依赖检测状态
        self._thread_local = threading.local()

    def _get_resolving_set(self) -> set:
        """获取当前线程的循环依赖检测集合"""
        if not hasattr(self._thread_local, 'resolving'):
            self._thread_local.resolving = set()
        return self._thread_local.resolving

    def bind(self,
             token: Union[str, Type[T], Token[T]], 
             provider: Provider,
             scope: str = "transient") -> 'Container':
        """
        绑定Token到提供者
        
        Args:
            token: 依赖标识
            provider: 提供者实例
            scope: 作用域名称
        
        Returns:
            容器实例（支持链式调用）
        """
        if not isinstance(token, Token):
            token = create_token(token)
        
        with self._lock:
            self._bindings[token] = provider
            self._scopes[token] = scope
        
        return self
    
    def bind_class(self, 
                   token: Union[str, Type[T], Token[T]], 
                   cls: Type[T],
                   scope: str = "transient",
                   *args, **kwargs) -> 'Container':
        """绑定Token到类"""
        provider = ClassProvider(cls, *args, **kwargs)
        return self.bind(token, provider, scope)
    
    def bind_factory(self, 
                     token: Union[str, Type[T], Token[T]], 
                     factory: Callable[..., T],
                     scope: str = "transient",
                     *args, **kwargs) -> 'Container':
        """绑定Token到工厂函数"""
        provider = FactoryProvider(factory, *args, **kwargs)
        return self.bind(token, provider, scope)
    
    def bind_value(self, 
                   token: Union[str, Type[T], Token[T]], 
                   value: T) -> 'Container':
        """绑定Token到值"""
        provider = ValueProvider(value)
        return self.bind(token, provider, "singleton")
    
    def bind_singleton(self, 
                       token: Union[str, Type[T], Token[T]], 
                       provider: Provider) -> 'Container':
        """绑定Token到单例提供者"""
        return self.bind(token, provider, "singleton")
    
    def get(self, token: Union[str, Type[T], Token[T]]) -> T:
        """
        获取依赖实例
        
        Args:
            token: 依赖标识
        
        Returns:
            依赖实例
        
        Raises:
            DependencyError: 依赖未注册或解析失败
            CircularDependencyError: 检测到循环依赖
        """
        if not isinstance(token, Token):
            token = create_token(token)
        
        # 检查循环依赖（使用线程本地存储）
        token_key = str(token)
        resolving_set = self._get_resolving_set()

        if token_key in resolving_set:
            raise CircularDependencyError(f"检测到循环依赖: {token}")

        try:
            resolving_set.add(token_key)
            return self._resolve(token)
        finally:
            resolving_set.discard(token_key)
    
    def _resolve(self, token: Token) -> Any:
        """内部解析方法"""
        with self._lock:
            # 检查是否已有实例（用于单例）
            if token in self._instances:
                return self._instances[token]
            
            # 查找提供者
            provider = self._find_provider(token)
            if not provider:
                raise DependencyError(f"未找到Token的绑定: {token}")
            
            # 获取作用域
            scope_name = self._scopes.get(token, "transient")
            scope = scope_manager.get(scope_name)
            if not scope:
                raise DependencyError(f"未找到作用域: {scope_name}")
            
            # 通过作用域获取实例
            instance = scope.get(str(token), lambda: provider.provide(self))
            
            # 对于单例，缓存实例
            if scope_name == "singleton":
                self._instances[token] = instance
            
            # 调用构造后回调
            self._call_post_construct(instance)
            
            return instance
    
    def _find_provider(self, token: Token) -> Optional[Provider]:
        """查找提供者"""
        # 先在当前容器查找
        if token in self._bindings:
            return self._bindings[token]
        
        # 再在父容器查找
        if self.parent:
            return self.parent._find_provider(token)
        
        return None
    
    def _call_post_construct(self, instance: Any) -> None:
        """调用构造后回调"""
        cls = instance.__class__
        for attr_name in dir(cls):
            attr = getattr(cls, attr_name)
            if callable(attr) and hasattr(attr, '_di_post_construct'):
                method = getattr(instance, attr_name)
                method()
    
    def _call_pre_destroy(self, instance: Any) -> None:
        """调用销毁前回调"""
        cls = instance.__class__
        for attr_name in dir(cls):
            attr = getattr(cls, attr_name)
            if callable(attr) and hasattr(attr, '_di_pre_destroy'):
                method = getattr(instance, attr_name)
                method()
    
    def has(self, token: Union[str, Type[T], Token[T]]) -> bool:
        """检查是否有Token的绑定"""
        if not isinstance(token, Token):
            token = create_token(token)
        
        return self._find_provider(token) is not None
    
    def remove(self, token: Union[str, Type[T], Token[T]]) -> bool:
        """移除Token的绑定"""
        if not isinstance(token, Token):
            token = create_token(token)
        
        with self._lock:
            removed = False
            
            if token in self._bindings:
                # 调用销毁前回调
                if token in self._instances:
                    self._call_pre_destroy(self._instances[token])
                    del self._instances[token]
                
                del self._bindings[token]
                del self._scopes[token]
                removed = True
            
            return removed
    
    def clear(self) -> None:
        """清空容器"""
        with self._lock:
            # 调用所有实例的销毁前回调
            for instance in self._instances.values():
                self._call_pre_destroy(instance)
            
            self._bindings.clear()
            self._scopes.clear()
            self._instances.clear()
    
    def create_child(self) -> 'Container':
        """创建子容器"""
        return Container(parent=self)
    
    def list_bindings(self) -> List[str]:
        """列出所有绑定的Token"""
        bindings = list(self._bindings.keys())
        if self.parent:
            bindings.extend(self.parent.list_bindings())
        return [str(token) for token in set(bindings)]
    
    def auto_register(self, cls: Type[T]) -> 'Container':
        """自动注册类"""
        # 检查是否有依赖注入元数据
        if not hasattr(cls, '_di_injectable'):
            raise DependencyError(f"类 {cls.__name__} 没有@Injectable装饰器")
        
        # 获取Token
        token = getattr(cls, '_di_token', create_token(cls))
        
        # 获取作用域
        scope = getattr(cls, '_di_scope', 'transient')
        
        # 创建提供者
        provider = ClassProvider(cls)
        
        # 绑定
        return self.bind(token, provider, scope)
    
    def scan_package(self, package_name: str) -> 'Container':
        """扫描包并自动注册组件"""
        import importlib
        import pkgutil
        
        try:
            package = importlib.import_module(package_name)
            
            for importer, modname, ispkg in pkgutil.iter_modules(package.__path__, package.__name__ + "."):
                try:
                    module = importlib.import_module(modname)
                    
                    # 扫描模块中的类
                    for attr_name in dir(module):
                        attr = getattr(module, attr_name)
                        
                        if (isinstance(attr, type) and 
                            hasattr(attr, '_di_component')):
                            self.auto_register(attr)
                            
                except ImportError:
                    continue
                    
        except ImportError:
            raise DependencyError(f"无法导入包: {package_name}")
        
        return self


class Injector:
    """
    依赖注入器
    
    提供全局的依赖注入功能。
    """
    
    _instance: Optional['Injector'] = None
    _lock = threading.Lock()
    
    def __new__(cls) -> 'Injector':
        """单例模式"""
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        if not hasattr(self, '_initialized'):
            self.container = Container()
            self._initialized = True
    
    def bind(self, *args, **kwargs) -> 'Injector':
        """绑定依赖"""
        self.container.bind(*args, **kwargs)
        return self
    
    def bind_class(self, *args, **kwargs) -> 'Injector':
        """绑定类"""
        self.container.bind_class(*args, **kwargs)
        return self
    
    def bind_factory(self, *args, **kwargs) -> 'Injector':
        """绑定工厂"""
        self.container.bind_factory(*args, **kwargs)
        return self
    
    def bind_value(self, *args, **kwargs) -> 'Injector':
        """绑定值"""
        self.container.bind_value(*args, **kwargs)
        return self
    
    def get(self, token: Union[str, Type[T], Token[T]]) -> T:
        """获取依赖"""
        return self.container.get(token)
    
    def has(self, token: Union[str, Type[T], Token[T]]) -> bool:
        """检查依赖"""
        return self.container.has(token)
    
    def remove(self, token: Union[str, Type[T], Token[T]]) -> bool:
        """移除依赖"""
        return self.container.remove(token)
    
    def clear(self) -> None:
        """清空容器"""
        self.container.clear()
    
    def auto_register(self, cls: Type[T]) -> 'Injector':
        """自动注册类"""
        self.container.auto_register(cls)
        return self
    
    def scan_package(self, package_name: str) -> 'Injector':
        """扫描包"""
        self.container.scan_package(package_name)
        return self
    
    def create_child_container(self) -> Container:
        """创建子容器"""
        return self.container.create_child()
    
    @classmethod
    def reset(cls) -> None:
        """重置单例实例（主要用于测试）"""
        with cls._lock:
            if cls._instance:
                cls._instance.clear()
                cls._instance = None

            # 清理全局作用域缓存
            from .scopes import scope_manager
            scope_manager.clear_all()


# 全局注入器实例
injector = Injector()
